

#include "SWD_flash.h"
#include "swd_host.h"
#include "debug_cm.h"
#include "swd_download_file.h"
#include "FlashOS.h"
#include <stdio.h>
#include <sys/stat.h>
#define LV_FILE_EXPLORER_PATH_MAX_LEN 256
struct offline_download_info_t offline_download_info;

char choose_device_path[LV_FILE_EXPLORER_PATH_MAX_LEN] = "/MCU/ST/STM32F4xx/STM32F4xx_512.FLM";
char choose_firmware_bin_path[LV_FILE_EXPLORER_PATH_MAX_LEN] = "/firmware/f407_100ms.bin";

// 判断当前是不是正在脱机下载中
static int8_t on_offline_swd_downloading = 0;

uint32_t parse_flm_from_file(char *_file_path);
extern uint32_t *get_flm_flash_blob_addr(void);
extern uint32_t get_flm_flash_algo_init_addr(void);
extern uint32_t get_flm_flash_algo_uninit_addr(void);
extern uint32_t get_flm_flash_algo_erase_chip_addr(void);
extern uint32_t get_flm_flash_algo_erase_sector_addr(void);
extern uint32_t get_flm_flash_algo_program_page_addr(void);

extern FlashDevice_T target_device;

void soft_reset_target(void)
{
    static uint32_t val;
    //    if (current_dap_mode == 1)
    //    {
    // Perform a soft reset
    if (!swd_read_word((0xe000e000) + 0x0D0C, &val))
    {
        printf("swd_read_word is %lx\r\n", val);
        return;
    }

    if (!swd_write_word((0xe000e000) + 0x0D0C, 0x05FA0000 | (val & SCB_AIRCR_PRIGROUP_Msk) | 0x00000004))
    {
        printf("swd_write_word is %lx\r\n", val);
        return;
    }
    //}
    printf("!!swd_read_word is %lx\r\n", val);
}

void buzzer_beep_set(uint16_t _tone_freq, uint8_t _volume)
{
}

int8_t is_on_offline_swd_downloading(void)
{
    return on_offline_swd_downloading;
}

static void staoffline_swd_download(void)
{
    on_offline_swd_downloading = 1;
}

static void stop_offline_swd_download(void)
{
    on_offline_swd_downloading = 0;
}

program_target_t flash_algo = {
    .init = 0X20000020 + 0X0000001D,         // Init
    .uninit = 0X20000020 + 0X0000004F,       // UnInit
    .erase_chip = 0X20000020 + 0X0000005D,   // EraseChip
    .erase_sector = 0X20000020 + 0X00000089, // EraseSector
    .program_page = 0X20000020 + 0X000000D5, // ProgramPage
    .sys_call_s = {
        .breakpoint = 0X20000001,
        .static_base = 0X20000C00,
        .stack_pointer = 0X20001000,
    },
    .program_buffer = 0x20000400,      // mem buffer location
    .algo_start = 0x20000000,          // location to write prog_blob in target RAM
    .algo_size = 0,                    // prog_blob size
    .algo_blob = 0,                    // address of prog_blob
    .program_buffer_size = 0x00000400, // ram_to_flash_bytes_to_be_written
};

// uint32_t Flash_Sect_Size = 8 * 1024;
// uint32_t Flash_Page_Size = 4 * 1024;
uint32_t Flash_StaAddr = 0x00000000;

static uint8_t file_read_buf[4 * 1024];
static uint8_t target_mcu_flash_read_buf[4 * 1024];

int8_t swd_download_update_flash_algo(char *_file_path)
{
    uint32_t flm_size = 0;
    flm_size = parse_flm_from_file(_file_path);
    if (flm_size == 0)
    {
        printf("parse flm from file error");
        return -1;
    }
    flash_algo.init = get_flm_flash_algo_init_addr();
    flash_algo.uninit = get_flm_flash_algo_uninit_addr();
    flash_algo.erase_chip = get_flm_flash_algo_erase_chip_addr();
    flash_algo.erase_sector = get_flm_flash_algo_erase_sector_addr();
    flash_algo.program_page = get_flm_flash_algo_program_page_addr();

    // flash_algo.sys_call_s.breakpoint = 0x20000001;
    // flash_algo.sys_call_s.static_base = 0x20000C00;
    // flash_algo.sys_call_s.stack_pointer = 0x20001000;

    flash_algo.program_buffer = 0x20000000 + flm_size;
    flash_algo.algo_start = 0x20000000;

    flash_algo.algo_size = flm_size;
    flash_algo.algo_blob = get_flm_flash_blob_addr();

    flash_algo.program_buffer_size = target_device.szPage; // 这个和实际flash写入时的容量相关

    flash_algo.sys_call_s.breakpoint = 0x20000000 + 1;
    flash_algo.sys_call_s.static_base = flash_algo.program_buffer + flash_algo.program_buffer_size;

    Flash_StaAddr = target_device.devAdr;
    return 0;
}

int32_t swd_download_from_file(char *_file_path)
{
    static int32_t fd = 0, read_size = 0, ret = 0;
    static char file_path[LV_FILE_EXPLORER_PATH_MAX_LEN] = {0};
    struct stat file_stat;

    uint32_t val;

    FILE *f = NULL;
    staoffline_swd_download();

    if (strlen(_file_path) < sizeof(file_path))
    {
        strncpy(file_path, _file_path, sizeof(file_path));
    }
    else
    {
        printf("file path too long");
        snprintf(offline_download_info.info_message, sizeof(offline_download_info.info_message), "%s", "path too long");
        goto error;
    }

    // 1 从TF卡中获取文件

    // 判断这个文件后缀是否为bin
    if (strstr(file_path, ".bin") == NULL)
    {
        printf("only support .bin file\n");
        snprintf(offline_download_info.info_message, sizeof(offline_download_info.info_message), "%s", "only .bin file");
        goto error;
    }
    /* 以只读模式打开文件*/
    f = fopen(file_path, "rb");
    if (f == NULL)
    {
        printf("open file failed\n");
        snprintf(offline_download_info.info_message, sizeof(offline_download_info.info_message), "%s", "open file faile");
        goto error;
    }
    else
    {
        printf("open file success\n");
        ret = stat(file_path, &file_stat);
        if (ret == 0)
            printf("%s file size = %ld\n", file_path, file_stat.st_size);
        else
            printf("%s file not fonud\n", file_path);
    }

    // 2 将FLM文件加载到目标单片机的RAM里
    swd_init_debug();
    printf("init debug\n");
    swd_set_target_state_hw(RESET_PROGRAM);
    printf("reset program\n");
    swd_read_dp(0x00, &val);
    printf("the chip IDCODE: %08lx\n", val);

    // 梁山派GD32F470需要再此处加入延时  而小华HC32F4A0不能有这个延时  还不清楚根因
    // TODO:考虑如何适配未来更多的芯片
    if (strstr(choose_device_path, "HC32") == NULL)
    {
        // thread_mdelay(200);
    }

    snprintf(offline_download_info.info_message, sizeof(offline_download_info.info_message), "%s", "load FLM file");
    if (target_flash_init(Flash_StaAddr) != ERROR_SUCCESS)
    {
        printf("target flash init error");
        goto error;
    }

    // 3 擦除目标单片机的Flash
    snprintf(offline_download_info.info_message, sizeof(offline_download_info.info_message), "%s", "erase flash");
    for (uint32_t addr = 0; addr < file_stat.st_size; addr += target_device.szPage)
    {
        target_flash_erase_sector(Flash_StaAddr + addr);
        offline_download_info.progress = (addr * 100) / file_stat.st_size;
        printf("erase sector is %08lx\n", Flash_StaAddr + addr);
    }

    // 4 读取目标flash是否为已经擦除成功
    //    for (uint32_t addr = 0; addr < file_stat.st_size; addr += 1024)
    //    {
    //        swd_read_memory(Flash_StaAddr + addr, file_read_buf, 1024);
    ////        ulog_hexdump("target_flash", 8,file_read_buf,sizeof(file_read_buf));
    //    }
    // 5 下载至目标单片机的Flash
    snprintf(offline_download_info.info_message, sizeof(offline_download_info.info_message), "%s", "download flash");
    for (uint32_t addr = 0; addr < file_stat.st_size; addr += target_device.szPage)
    {
        read_size = fread(file_read_buf, 1, target_device.szPage, f);

        printf("read size = %ld\n", read_size);
        offline_download_info.progress = (addr * 100) / file_stat.st_size;
        target_flash_program_page(Flash_StaAddr + addr, file_read_buf,
                                  target_device.szPage);
    }
    fclose(f);
    f = fopen(file_path, "rb"); // 重新打开文件要从头开始读
    // 6 读回校验
    snprintf(offline_download_info.info_message, sizeof(offline_download_info.info_message), "%s", "verify flash");
    for (uint32_t addr = 0; addr < file_stat.st_size; addr += target_device.szPage)
    {
        swd_read_memory(Flash_StaAddr + addr, target_mcu_flash_read_buf, target_device.szPage);

        read_size = fread(file_read_buf, 1, target_device.szPage, f);
        printf("file read size = %ld\n", read_size);
        offline_download_info.progress = (addr * 100) / file_stat.st_size;
        // ulog_hexdump("target_flash", 8,target_mcu_flash_read_buf,sizeof(target_mcu_flash_read_buf));
        //
        // ulog_hexdump("file", 8,file_read_buf,sizeof(file_read_buf));

        if (memcmp(target_mcu_flash_read_buf, file_read_buf,
                   file_stat.st_size - addr > target_device.szPage
                       ? target_device.szPage
                       : file_stat.st_size - addr) == 0)
        {
            printf("verify pass");
        }
        else
        {
            printf("verify fail");
            snprintf(offline_download_info.info_message, sizeof(offline_download_info.info_message), "%s", "verify fail");
            goto error;
        }
    }
    offline_download_info.progress = 100;
    snprintf(offline_download_info.info_message, sizeof(offline_download_info.info_message), "%s", "verify pass");
    fclose(f);
    // 7 让目标单片机开始运行
    soft_reset_target();

    offline_download_info.success_download_count += 1;

    stop_offline_swd_download();

    return 0;
    // 6 进入错误状态
error:
    fclose(f);
    printf("error occured!");
    snprintf(offline_download_info.info_message, sizeof(offline_download_info.info_message), "%s", "error occured!");

    stop_offline_swd_download();
    return -1;
}
